/*****************************************************************************\
Microsoft Research Asia
Copyright (c) Microsoft Corporation. All Rights Reserved.

Module Name:	ImageWrapper.h
Notes:				Wrapper Class for image in memory  
History:			Created by Rong Xiao rxiao@microsoft.com		Jun 2, 2006
\*****************************************************************************/

#pragma once
#include <assert.h>
#include <math.h>
#include "../CommonUsed/TFun.h"
#include "windows.h"

#define COLOR_SHIFT_BITS  24
#define COLORSCALE (1<<COLOR_SHIFT_BITS)
#define RED_SCALEWEIGHT    ((unsigned)(COLORSCALE*0.299+0.5))
#define GREEN_SCALEWEIGHT  ((unsigned)(COLORSCALE*0.587+0.5))
#define BLUE_SCALEWEIGHT   ((unsigned)(COLORSCALE*0.114+0.5))

inline BYTE RGB2GrayScale ( BYTE r, BYTE g, BYTE b )
{
	return BYTE( (RED_SCALEWEIGHT*r + GREEN_SCALEWEIGHT*g + BLUE_SCALEWEIGHT*b) >> COLOR_SHIFT_BITS);
}

//=============================================================================
// Image struct for local use
// A wrapper of image in memory
//=============================================================================
class CImageWrapper
{   
//Data
private:  
    unsigned m_uWidth;				//For CImageWrapper can be part of a large image, these two
    unsigned m_uHeight;				//value just show the width and height of the sub-image
	unsigned m_uImageWidth;
	unsigned m_uImageHeight;

    //info for source image
    const BYTE *m_pbImage;			//Image Buffer

	unsigned m_uPixelBytes;			//should be 3: 24-bit
	int m_iStride;				//the stride for scan lines
    
	int m_iColorSequence;		//the sequence of color, RGB or BGR
    
	unsigned m_uRedOffset;         
    unsigned m_uGreenOffset;
    unsigned m_uBlueOffset;
  

// Functions
public:
	CImageWrapper() :	m_uWidth(0),     m_uHeight(0),      m_uImageWidth(0),    m_uImageHeight(0),
						m_iStride(0),    m_uPixelBytes(3),  m_iColorSequence(0), m_pbImage(NULL),  
						m_uRedOffset(0), m_uGreenOffset(0), m_uBlueOffset(0)	 {}
	~CImageWrapper() {};

	unsigned ImageWidth() const { return m_uImageWidth; }
	unsigned ImageHeight() const { return m_uImageHeight; }
    unsigned Height() const { return m_uHeight; }
    unsigned Width() const { return m_uWidth; }
    unsigned GetBytesPerPixel() const { return m_uPixelBytes; }
    int GetStride() const { return m_iStride; }
    int GetColorSequence() const { return m_iColorSequence;}

    inline const BYTE* GetBuffer() const { return m_pbImage; }
	BYTE * RowPtr ( unsigned  y);
	const BYTE * RowPtr ( unsigned  y) const;
    const BYTE *GetPixelPtr(unsigned x, unsigned y) const;
	BYTE *GetPixelPtr(unsigned x, unsigned y);

    HRESULT CreateImage(const UINT uiW,         
                        const UINT uiH,               
                        const BYTE *pbImage,  
                        const int iBytesPerPixel,     
                        int iStride,            
                        const int iColorSequence,     
                        LPCRECT pRectImage);


	BYTE* CropRotatedRect(const RECT rect, const float flRotate) const;

    unsigned GetGrayPixel(const unsigned x, const unsigned y) const;

    inline unsigned RedOffset() const { return m_uRedOffset; }
    inline unsigned GreenOffset() const { return m_uGreenOffset; }
    inline unsigned BlueOffset() const { return m_uBlueOffset; }

	const BYTE * GetRLayer(int x, int y) const;
	BYTE * GetRLayer(int x, int y);
	const BYTE * GetGLayer(int x, int y) const;
	BYTE * GetGLayer(int x, int y);
	const BYTE * GetBLayer(int x, int y) const;
	BYTE * GetBLayer(int x, int y);
};


//===========================================================================
//    CreateImage
//        Create a Face Flat Image with Given Parameters
// 
//    Parameters
//        uiW            
//            Image width
//        uiH            
//            Image height
//        pbImage        
//            The buffer of image
//        iBytesPerPixel 
//            Bytes per pixel, 3 or 4
//        iStride       
//            Stride in byte for every scan line, 0 for DWORD alignment, <0 for buttom-up order
//        iColorSequence 
//            0 for RGB, 1 for BGR
//        pRectImage     
//            Pointer to a rectangle that specifies the range that 
//            CImageWrapper associates with. NULL for entire image    
// 
//   Return Values
//        S_OK indicates success. Otherwise for failure
//===========================================================================
inline HRESULT CImageWrapper::CreateImage(const UINT uiW,
                                    const UINT uiH,
                                    const BYTE *pbImage,
                                    const int iBytesPerPixel,
                                    int iStride,
                                    const int iColorSequence,
                                    LPCRECT pRectImage)
{       
    HRESULT hr = S_OK;
    RECT rImage = {0, 0, 0, 0};

	assert(iStride);

	if (iStride == 0)
	{
		iStride = iBytesPerPixel * uiW;
		if (iStride % 4)
		{
			iStride = ((iStride / 4) + 1) * 4;
		}
	}

	unsigned uStride = (unsigned) ( (iStride>0)?  iStride : (-iStride) );
    m_iColorSequence = iColorSequence;

	m_uImageWidth = uiW;
	m_uImageHeight = uiH;

    //check for invalid parameters
    if ((uiW == 0) || (uiH == 0) || (iBytesPerPixel == 0) || (uiW * iBytesPerPixel >  uStride))
    {
        hr = E_INVALIDARG;
    }

    if (SUCCEEDED(hr) && (NULL == pbImage) )
    {
        hr = E_INVALIDARG;
    }

    if (SUCCEEDED(hr) && ((iColorSequence != 0) && (iColorSequence != 1)))
    {
        hr = E_INVALIDARG;
    }

    if (SUCCEEDED(hr) && pRectImage)
    {
        if ((pRectImage->left < 0) || (pRectImage->top < 0)
            || (pRectImage->right > (int)uiW) || (pRectImage->bottom > (int)uiH)
            || (pRectImage->right <= pRectImage->left)
            || (pRectImage->bottom <= pRectImage->top))
        {
             hr = E_INVALIDARG;
        }
        else
        {
            CopyMemory(&rImage, pRectImage, sizeof(RECT));
        }
    }
    else
    {
        rImage.left = rImage.top = 0;
        rImage.right = uiW;
        rImage.bottom = uiH;
    }

    if (SUCCEEDED(hr))
    {
        m_uWidth = (unsigned) (rImage.right - rImage.left);
        m_uHeight = (unsigned) (rImage.bottom - rImage.top);
        m_uPixelBytes = (unsigned) iBytesPerPixel;

        //Set color sequence
        if (m_uPixelBytes == 1)
        {
            m_uRedOffset = 0;
            m_uGreenOffset = 0;
            m_uBlueOffset = 0;
        }
        else if (iColorSequence)
        {
            m_uRedOffset = 2;
            m_uGreenOffset = 1;
            m_uBlueOffset = 0;
        }
        else
        {
            m_uRedOffset = 0;
            m_uGreenOffset = 1;
            m_uBlueOffset = 2;
        }
   
        m_iStride = iStride;
        m_pbImage = (const BYTE*)pbImage + m_iStride * rImage.top  + m_uPixelBytes * rImage.left;
    }
    return hr;
}

//===========================================================================
//    RowPtr
//        Get the pixel pointer in one scanline
//
//    Parameters
//        y      The row number
//
//    Return Values
//        The pointer of specified pixel
//===========================================================================
inline BYTE * CImageWrapper::RowPtr(unsigned y)
{
    //Set the start pointers for each scan line
	assert (y>=0 && y<m_uHeight && !IsBadReadPtr(m_pbImage + y * m_iStride, m_iStride));
	return const_cast<BYTE*>(m_pbImage) + y * m_iStride;
}

inline const BYTE * CImageWrapper::RowPtr(unsigned y) const
{
    //Set the start pointers for each scan line
	assert (y>=0 && y<m_uHeight && !IsBadReadPtr(m_pbImage + y * m_iStride, m_iStride));
	return m_pbImage + y * m_iStride;
}

//===========================================================================
//    GetPixelPtr
//        Get the pixel pointer in one location
//
//    Parameters
//        x, y
//            The location
//
//    Return Values
//        The pointer of specified pixel
//===========================================================================
inline const BYTE *CImageWrapper::GetPixelPtr(const unsigned x, const unsigned y) const
{
    assert ( (x < m_uWidth) && (y < m_uHeight) );
    return  RowPtr(y) + x * m_uPixelBytes;
}
inline BYTE *CImageWrapper::GetPixelPtr(const unsigned x, const unsigned y) 
{
    assert ( (x < m_uWidth) && (y < m_uHeight) );
    return  RowPtr(y) + x * m_uPixelBytes;
}

//===========================================================================
//    GetGrayPixel
//        Get gray level of one pixel
//
//    Parameters
//        x, y
//            The location of the pixel
//
//    Return Values
//        Gray leval
//===========================================================================
inline  unsigned CImageWrapper::GetGrayPixel(unsigned x, unsigned y) const
{     
    assert ( x < m_uWidth &&  y < m_uHeight);
    const BYTE* pBase = RowPtr(y) + x * m_uPixelBytes;

    return RGB2GrayScale(pBase[m_uRedOffset], pBase[m_uGreenOffset],pBase[m_uBlueOffset]);
}

//===========================================================================
//    CropRotatedRect
//        Crop a Rotated Rectange in the Face Image
//
//    Parameters
//        rect
//            The location of the rectangle
//        flRotate
//            The degree of rotation
//
//    Return Values
//        The pointer to the rotated rectangle, NULL for error
//
//    Note
//        Caller should Free Memory 
//===========================================================================
inline BYTE* CImageWrapper::CropRotatedRect(RECT rect, float flRotate) const
{
    int iU, iV;
    double dCos, dSin, dDU, dDV;
    double dCx, dCy;        //Center of the rect * 2
    long scaledCos, scaledSin, scaledDU, scaledDV;
    long scaledWSrc, scaledHSrc, scaledCx, scaledCy;
    const long scaleFactor = 65536L;
    int nWSrc, nHSrc, nWDst, nHDst;
    BYTE* pBuf;

    nWSrc = m_uWidth;
    nHSrc = m_uHeight;
    nWDst = rect.right - rect.left;
    nHDst = rect.bottom - rect.top;
    dCx = rect.right + rect.left;
    dCy = rect.bottom + rect.top;
    assert (nWDst > 0 && nHDst > 0);
    pBuf = new BYTE[nWDst * nHDst];
    if (pBuf)
    {
        dCos = cos(-flRotate);
        dSin = sin(-flRotate);
        dDU = (dCx - nWDst * dCos - nHDst * dSin) / 2;
        dDV = (dCy + nWDst * dSin - nHDst * dCos) / 2;
        
        scaledWSrc = (long) nWSrc * scaleFactor;
        scaledHSrc = (long) nHSrc * scaleFactor;
        scaledCx = (long) dCx * scaleFactor;
        scaledCy = (long) dCy * scaleFactor;
    
        scaledCos  = (long) (dCos * scaleFactor + 0.5);
        scaledSin  = (long) (dSin * scaleFactor + 0.5);
        scaledDU   = (long) (dDU * scaleFactor + 0.5);
        scaledDV   = (long) (dDV * scaleFactor + 0.5);
    
        long vSrc, uSrc;
        BYTE *pDst;
        int nX, nY;
        BYTE r, g, b;
    
        for (iV = 0; iV < nHDst; iV++)
        {    //For row
            uSrc = iV * scaledSin + scaledDU;
            vSrc = iV * scaledCos + scaledDV;
            
            pDst = pBuf + iV * nWDst;
   
            for (iU = 0; iU < nWDst; iU++)
            {    //For Col
                if ((uSrc < 0) || (vSrc < 0) || (uSrc >= scaledWSrc) || (vSrc >= scaledHSrc))
                {
                    *pDst++ = 255;
                }
                else
                {
                    nX = uSrc >> 16;
                    nY = vSrc >> 16;
					const BYTE* pRow = GetPixelPtr(nX,nY);
                    r = pRow[m_uRedOffset];
                    g = pRow[m_uGreenOffset];
                    b = pRow[m_uBlueOffset];
                    *pDst++ = RGB2GrayScale(r,g,b);
                }
                uSrc += scaledCos;
                vSrc -= scaledSin;
            }
        }
    }
    return pBuf;
}    

//===========================================================================
//    GetRLayer
//        Get red layer
//
//    Parameters
//        x, y
//            The location
//
//    Return Values
//        The pointer of specified pixel
//===========================================================================
inline const BYTE * CImageWrapper::GetRLayer(int x, int y) const
{
	const BYTE * pBuf = GetPixelPtr(x, y);
	if (pBuf)
	{
		pBuf += m_uRedOffset;
	}
	return pBuf;
}
inline BYTE * CImageWrapper::GetRLayer(int x, int y) 
{
	BYTE * pBuf = GetPixelPtr(x, y);
	if (pBuf)
	{
		pBuf += m_uRedOffset;
	}
	return pBuf;
}

//===========================================================================
//    GetGLayer
//        Get green layer
//
//    Parameters
//        x, y
//            The location
//
//    Return Values
//        The pointer of specified pixel
//===========================================================================
inline const BYTE * CImageWrapper::GetGLayer(int x, int y) const
{
	const BYTE * pBuf = GetPixelPtr(x, y);
	if (pBuf)
	{
		pBuf += m_uGreenOffset;
	}
	return pBuf;
}
inline BYTE * CImageWrapper::GetGLayer(int x, int y)
{
	BYTE * pBuf = GetPixelPtr(x, y);
	if (pBuf)
	{
		pBuf += m_uGreenOffset;
	}
	return pBuf;
}

//===========================================================================
//    GetBLayer
//        Get blue layer
//
//    Parameters
//        x, y
//            The location
//
//    Return Values
//        The pointer of specified pixel
//===========================================================================
inline const BYTE * CImageWrapper::GetBLayer(int x, int y) const
{
	const BYTE * pBuf = GetPixelPtr(x, y);
	if (pBuf)
	{
		pBuf += m_uBlueOffset;
	}
	return pBuf;
}
inline  BYTE * CImageWrapper::GetBLayer(int x, int y) 
{
	BYTE * pBuf = GetPixelPtr(x, y);
	if (pBuf)
	{
		pBuf += m_uBlueOffset;
	}
	return pBuf;
}